#include "master.h"
#define NUM_PARAMS 10
#define PARAM_IP 1
#define PARAM_INST PARAM_IP+1
#define PARAM_IT PARAM_INST+1
#define PARAM_TAM_POOL PARAM_IT+1
#define PARAM_TAM_PADROES PARAM_TAM_POOL+1

sem_t mutex;
int it;
int ndemandas;
int tam_pool;
int semente;

void envia_padrao(int slave){
	if (padroes==NULL){
		//envia null pro slave
	}else{
			
		indice_rr = (indice_rr+1) % padroes->qnt;	
	}
}

void envia_solucao(int slave){

}

int iniciar_slaves(char *arq_ips){

}

void imprimir_solucao(){

}

void *espera_req(){
}


void minera(int qnt_padroes,int min_suporte,int ndemandas){
/*	Melhores *melhores=NULL;
	if(padroes!=NULL)freePadroes(padroes);
	
	double t_miner;

	padroes = (Padroes ++*)malloc(sizeof(Padroes));
	padroes->qnt=0;
	padroes->conjunto=NULL;

	melhores = gera_melhores(pool,ndemandas);

	findpatterns(*melhores,padroes,min_suporte, qnt_padroes,&t_miner);

	if(melhores!=NULL)freeMelhores(melhores);*/
}

void show_usage(){
	printf("use it\n");
}

t_aresta parseToAresta(int id){
	t_aresta aresta;
	if(id>0){
		aresta = arestas_ids[id];	
		aresta.id = id;
	}else{
		aresta.id = 0;
		aresta.origem = 0;
		aresta.destino=0;
	}
	//printf("%d = {%d , %d} %d\n",id , aresta.origem,aresta.destino,aresta.id);
	return aresta;
}

void parseToSolucao(t_aresta solucao[][3],int sol[][2],int tam){
	int i;

	for(i=1;i<=tam;i++){
		solucao[i][1] = parseToAresta(sol[i-1][0]);
		solucao[i][2] = parseToAresta(sol[i-1][1]);
	}
}

void parseToInt(int sol[][2],t_aresta solucao[][3],int tam){
	int i;
	//printf("parsing ... %d\n",tam);
	for(i=1;i<=tam;i++){
	//	printf("parsing %d... \n",i);
	//	printf("(%d %d) = %d\n",solucao[1][1].origem,solucao[1][1].destino,ids[solucao[1][1].origem][solucao[1][1].destino]);
		sol[i-1][0] = ids[solucao[i][1].origem][solucao[i][1].destino];
		sol[i-1][1] = ids[solucao[i][2].origem][solucao[i][2].destino];
	}
}

int get_solucao_pool_pr(int sol[][2]){
	int ind;
	if(tam_pool > 1){
		if(tam_pool < MAX_TAM_POOL)
			ind = get_rand_ij(&semente, 1, (tam_pool - 1));
		else
			ind = get_rand_ij(&semente, 1, tam_pool);

		parseToInt(sol,pool_solucao[ind],ndemandas);
		return pool_custo[ind];
	}else
		return 0;

}

void inserir_pool_pr(t_aresta solucao[MAX_DEMANDA+1][3],int custo){
	int ind_pool;
	if(!PertencePool(solucao, pool_solucao, ndemandas, tam_pool)){
		if(tam_pool < MAX_TAM_POOL){
			tam_pool++; 
			CopiaSolucao(solucao, pool_solucao[tam_pool], ndemandas);
			pool_custo[tam_pool] = custo;
		}else{
			ind_pool = EncontraIndiceMaiorCusto(custo, MAX_TAM_POOL, pool_custo);
			if(ind_pool){
				CopiaSolucao(solucao, pool_solucao[ind_pool], ndemandas);
				pool_custo[ind_pool] = custo;
				ind_pool = 0;
			}
		}
	}
}

void *atende_req(void* req_a){
	Requisicao req = *((Requisicao*) (req_a));
	//SolucaoPool solucao;
	int tipo = req.tipo;
	//sem_wait(&mutex);
	int tamanho,custo,i,j;
	MPI_Status status;
	tamanho = req.tamanho;
	custo = req.custo;
	printf("req-t: %d\n", req.tipo); 
	//int** array = req.array;
	if(tipo == RECEBE_SOLUCAO){
		//printf("master esperando tamanho solucao \n");
		//MPI_Recv(&tamanho, sizeof(tamanho), MPI_INT, req.id_slave, 1, MPI_COMM_WORLD,&status);
		//printf("Tamanho Solucao  %d\n",tamanho);
		//int array[tamanho][2];
		//printf("master esperando solucao \n");
		//MPI_Recv(array, tamanho*2, MPI_INT, req.id_slave, 1, MPI_COMM_WORLD,&status);
		//printf("Recebeu Solucao de %d\n",req.id_slave);

		//for(i=0;i<tamanho;i++){
		//	printf("(%d,%d), ",array[i][0],array[i][1]);
		//}
		//printf("\n");
		//printf("master esperando custo \n");
		//MPI_Recv(&custo, sizeof(custo), MPI_INT, req.id_slave, 1, MPI_COMM_WORLD,&status);

		t_aresta solucao[MAX_DEMANDA+1][3];

		parseToSolucao(solucao,req.array,tamanho);
		inserir_pool_pr(solucao,custo);

		/*if (verifica_insercao(pool,custo)){
			printf("inseriu no pool MD\n");
			inserir_solucao_pool(pool,solucao,custo,ndemandas);
		}*/

		it +=1;
		printf("Custo: %d\n",custo);
	}else if(tipo == RECEBE_SOLUCAO_BL){
		//MPI_Recv(&tamanho, sizeof(tamanho), MPI_INT, req.id_slave, 1, MPI_COMM_WORLD,&status);
		//printf("Tamanho Solucao BL  %d\n",tamanho);
		//int array[tamanho][2];
		//MPI_Recv(array, tamanho*2, MPI_INT, req.id_slave, 1, MPI_COMM_WORLD,&status);
		//printf("Recebeu Solucao BL de %d\n",req.id_slave);

		//for(i=0;i<tamanho;i++){
			//printf("(%d,%d), ",array[i][0],array[i][1]);
		//}
	//	printf("\n");
		//MPI_Recv(&custo, sizeof(custo), MPI_INT, req.id_slave, 1, MPI_COMM_WORLD,&status);
		//printf("Custo BL: %d\n",custo);
		t_aresta solucao[MAX_DEMANDA+1][3];
		parseToSolucao(solucao,req.array,tamanho);
		//printf("parse done!\n");

		inserir_pool_pr(solucao,custo);

	}else if(tipo==ENVIA_SOLUCAO){
		tamanho = ndemandas;
		int solucao[tamanho][2];

		custo = get_solucao_pool_pr(solucao);
		if(custo==0)tamanho = 0;

		printf("enviando tamanho matriz\n");
		MPI_Send(&tamanho, sizeof(int), MPI_INT, req.id_slave, 1, MPI_COMM_WORLD);
		
		if(tamanho>0){

			printf("montando solucao para pr\n");

			printf("enviando solucao para pr\n");
			MPI_Send(solucao, tamanho*2, MPI_INT, req.id_slave, 1, MPI_COMM_WORLD);
			MPI_Send(&custo, sizeof(custo), MPI_INT, req.id_slave, 1, MPI_COMM_WORLD);

		}

	}else if(tipo==ENVIA_PADRAO){
		tamanho = 0;
		MPI_Send(&tamanho, sizeof(int), MPI_INT, req.id_slave, 1, MPI_COMM_WORLD);
		if(tamanho>0){
			int padrao[tamanho];
			for(i=0;i<tamanho;i++)
				padrao[i] = i;

			MPI_Send(padrao, tamanho, MPI_INT, req.id_slave, 1, MPI_COMM_WORLD);
		}

	}
	//sem_post(&mutex);
	//pthread_exit(NULL);
}


void init_master(int narestas, int ndemandas_in,int semente_in,int total_it){
	ndemandas = ndemandas_in;
	semente = semente_in;
	MPI_Status status;
	int i,j,k;
	int tipo;
	int tamanho,n,m;
	inicia_buffer(10000);
	tam_pool=0;


//	int dest, tag;
	char *buffer;
	
	int rc;
	
	it=0;
	while(it<total_it){
		printf("master esperando tipo %d/%d\n",it,total_it);
		MPI_Recv(&tipo, sizeof(tipo), MPI_INT, MPI_ANY_SOURCE, 1, MPI_COMM_WORLD,&status);
		register int bkp_tipo = tipo;
		printf("connected to %d (%d %d)\n",status.MPI_SOURCE,tipo,bkp_tipo);
		
		Requisicao req;
	
		
		
		if (tipo==RECEBE_SOLUCAO||tipo==RECEBE_SOLUCAO_BL){
			int tamanho, custo;
			printf("master esperando tamanho solucao \n");			
			printf("antes tamanho tipo: %d %d %d\n",tipo,req.tipo,bkp_tipo);
			MPI_Recv(&tamanho, sizeof(tamanho), MPI_INT, status.MPI_SOURCE, 1, MPI_COMM_WORLD,&status);
			printf("depois tamanho tipo: %d %d %d\n",tipo,req.tipo,bkp_tipo);
			printf("Tamanho Solucao  %d\n",tamanho);
			//int array[tamanho][2];
			printf("master esperando solucao %d\n",status.MPI_SOURCE);
			MPI_Recv(req.array, tamanho*2, MPI_INT, status.MPI_SOURCE, 1, MPI_COMM_WORLD,&status);
			printf("master esperando custo \n");
			MPI_Recv(&custo, sizeof(custo), MPI_INT, status.MPI_SOURCE, 1, MPI_COMM_WORLD,&status);
			printf("Custo: %d\n",custo);
			//req.array = array;
			req.tamanho = tamanho;
			req.custo = custo;
			req.id_slave = status.MPI_SOURCE;
			req.tipo = bkp_tipo;
			printf("tipo: %d %d %d\n",tipo,req.tipo,bkp_tipo);
			pthread_t t;
			rc = pthread_create(&t, NULL, atende_req, (void *)&req);
			if (rc){
				fprintf(stderr,"ERROR; return code from pthread_create() is %d\n", rc);
				exit(-1);
			}
		}else{
			req.tipo = tipo;
			req.id_slave = status.MPI_SOURCE;
			pthread_t t;
			rc = pthread_create(&t, NULL, atende_req, (void *)&req);
			if (rc){
				fprintf(stderr,"ERROR; return code from pthread_create() is %d\n", rc);
				exit(-1);
			}
		}
	}

}

/*
int main(int argc, char *argv[]){
	if(argc!=NUM_PARAMS){
		show_usage();
		return 1;
	}

	Requisicao req;
   	pthread_t *threads;
	char inst[100];
	strcpy(inst,argv[PARAM_INST]);
	int num_padroes = atoi(argv[PARAM_TAM_PADROES]);
	int pool_limit = atoi(argv[PARAM_TAM_POOL]);
	int n_slaves;
//	int pool_limit_pr = atoi(argv[PARAM_TAM_POOL_PR]);
	num_it = atoi(argv[PARAM_IT]);
	sem_init(&mutex, 1, 1);

	pool = initPool(pool_limit);
//	pool_pr = initPool(pool_limit_pr);
	padroes=NULL;
	slaves=NULL;
	n_slaves = iniciar_slaves(argv[PARAM_IP]);
	int num_t = 2*n_slaves+2;
	threads = (pthread_t*) malloc(num_t*sizeof(pthread_t));
	int prox_t=num_t, rc;
	inicia_buffer(n_slaves);
	SolucaoPool solucao;

	while (num_it > 0){
		req = buffer_pop();
      		rc = pthread_create(&threads[(prox_t=(prox_t+1)%num_t)], NULL, atende_req, (void *)&req);
		if (rc){
         		fprintf(stderr,"ERROR; return code from pthread_create() is %d\n", rc);
       			exit(-1);
		}

	}
	imprimir_solucao();
//insira aqui o código para matar os clientes que estão rodando ainda
	freePool(pool);
	freeBuffer();
	free(threads);


	return 0;
}*/
